package cn.com.zhaoweiping.ie;

import cn.com.zhaoweiping.ie.RowReadStatus.Status;
import cn.com.zhaoweiping.ie.convert.CellDataConverter;
import cn.com.zhaoweiping.ie.convert.HeaderConverter;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.CellData;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 读取Excel数据<br>
 *
 * @author 冰羽
 * @version 2.2.0
 */
public class ExcelReadAnalysisListener<T> extends AnalysisEventListener<Map<Integer, Object>> {

  private SimpleReadContext readContext;
  /** 解析读取的过程中是否出错 */
  private boolean error = false;
  /** 结果回调函数 */
  private ReadCallback readCallback;
  /** 数据转换函数 */
  private CellDataConverter cellDataConverter;
  /** 表头转换函数 */
  private HeaderConverter headerConverter;

  /** 读取数据后缓存的量 */
  private int batchSize;
  /** 读取数据后缓存的值 */
  private List<RowWrapper> data;

  /** 表头 */
  private Map<Integer, List<ExcelField>> excelFields;
  /** Excel Sheet原始表头（只有一个） - invokeHead Method */
  private Map<Integer, CellData> originHeader;

  public ExcelReadAnalysisListener(
      SimpleReadContext readContext,
      HeaderConverter headerConverter,
      CellDataConverter cellDataConverter,
      ReadCallback readCallback) {
    this(readContext, headerConverter, cellDataConverter, readCallback, 10);
  }

  public ExcelReadAnalysisListener(
      SimpleReadContext readContext,
      HeaderConverter headerConverter,
      CellDataConverter cellDataConverter,
      ReadCallback readCallback,
      int batchSize) {
    this.readContext = readContext;
    this.readCallback = readCallback;
    this.cellDataConverter = cellDataConverter;
    this.headerConverter = headerConverter;
    this.batchSize = batchSize;
    this.data = new ArrayList<>(batchSize);
  }

  @Override
  public void onException(Exception exception, AnalysisContext context) throws Exception {
    this.readCallback.callback(exception, null, readContext, context);
    this.readContext.setStatus(Status.UNFINISHED);
    this.error = true;
  }

  @Override
  public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) {
    this.excelFields = this.headerConverter.convert(headMap, context);
    this.originHeader = Maps.newHashMap();
    BeanUtil.copyProperties(headMap, originHeader, CopyOptions.create());
  }

  @Override
  public void invoke(Map<Integer, Object> data, AnalysisContext context) {
    // 设置为数据读取状态
    this.readContext.setStatus(Status.READING);
    RowWrapper wrapper =
        this.cellDataConverter.convert(this.excelFields, this.originHeader, data, context);

    if (ObjectUtil.isNotEmpty(wrapper) && wrapper.isNotEmpty()) {
      this.data.add(wrapper);
    }

    if (this.data.size() >= this.batchSize) {
      this.readCallback.callback(null, this.data, readContext, context);
      this.data.clear();
    }
  }

  @Override
  public void doAfterAllAnalysed(AnalysisContext context) {
    // 处理 invoke 中处理剩余的数据
    // 先克隆数据
    List<RowWrapper> list = ObjectUtil.clone(this.data);

    // 清空临时数据
    this.data.clear();
    this.readContext.setStatus(Status.FINISH);

    // 判断是否到最后一个Sheet、最后一个Sheet读取完成之后标识Excel读取完成
    int sheetNo = context.readSheetHolder().getSheetNo();
    if (sheetNo == this.readContext.getSheetCounts() - 1) {
      this.readContext.setExcelStatus(Status.FINISH);
    }

    // 在读取完一个Sheet之后回调
    this.readCallback.callback(null, list, this.readContext, context);
  }

  @Override
  public boolean hasNext(AnalysisContext context) {
    return this.error == false || this.readContext.readStatus() == Status.FINISH;
  }
}
